package com.shadow.study.java.base.util.captcha;

import lombok.extern.slf4j.Slf4j;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * 验证码生成工具类
 *
 * <p>
 *     - 调用 CaptchaUtils 类，创建对象
 *     - 调用 generatorVCode() 生成验证码
 *     - 调用 generatorVCodeImage() 生成图片
 * </p>
 * @author shadow
 * */
@Slf4j
public final class CaptchaUtils {

    /* 验证码来源 */
    private static final char[] CODE = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
            'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R',
            'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };
    /* 验证码长度 */
    private static final int CODE_LENGTH      = 4;
    /* 验证码图片字体大小 */
    private static final int FONT_SIZE        = 21;
    /* 干扰线条数 */
    private static final int DISTURB_LINE_NUM = 3;
    /* 验证码背景色 */
    private static final Color COLOR          = Color.white;


    // ------------------------------- tips start -------------------------------
    //
    // It is not recommended to make changes unless you understand what it is
    //
    // -------------------------------  tips end  -------------------------------

    /**
     * 字体
     */
    private static final String[] FONT_NAMES = new String[] {
            "黑体", "宋体", "Courier", "Arial", "Verdana", "Times",
            "Tahoma", "Georgia"
    };
    /* 字体样式 */
    private static final int[] FONT_STYLES = new int[] {
            Font.BOLD,
            Font.ITALIC|Font.BOLD
    };
    /* 验证码图片宽度 */
    private static final int WIDTH  = (FONT_SIZE + 1) * CODE_LENGTH + 10;
    /* 验证码图片高度 */
    private static final int HEIGHT = FONT_SIZE + 12;

    private CaptchaUtils(){
    }

    /**
     * 生成验证码
     * @return 验证码
     */
    public static String generatorCode() {
        Random ran = new Random();
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < CODE_LENGTH; i++) {
            int index = ran.nextInt(CODE.length);
            sb.append(CODE[index]);
        }
        return sb.toString();
    }

    /**
     * 生成验证码图片.
     * @param code       要画的验证码
     * @param isDrawline 是否画干扰线
     * @return 验证码图片
     */
    public static BufferedImage generatorVCodeImage(final String code, final Boolean isDrawline){
        // 创建验证码图片
        BufferedImage codeImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics g = codeImage.getGraphics();
        g.setColor(COLOR); //填充背景色
        g.fillRect(0, 0, WIDTH, HEIGHT);
        if(Boolean.TRUE.equals(isDrawline)) {
            drawDisturbLine(g);
        }
        // 用于生成伪随机数
        Random ran = new Random();
        //在图片上画验证码
        for(int i = 0;i < code.length();i++){
            //设置字体
            g.setFont(new Font(
                    FONT_NAMES[ran.nextInt(FONT_NAMES.length)],
                    FONT_STYLES[ran.nextInt(FONT_STYLES.length)],
                    FONT_SIZE
            ));
            g.setColor(getRandomColor()); // 随机生成颜色
            g.drawString(code.charAt(i)+"", i * FONT_SIZE + 10, FONT_SIZE + 5); // 画验证码
        }
        g.dispose(); //释放此图形的上下文以及它使用的所有系统资源
        return codeImage;
    }

    /**
     * 获得旋转字体的验证码图片
     * @param code     要画的验证码
     * @param drawline 是否画干扰线
     * @return 验证码图片
     */
    public static BufferedImage generatorRotateVCodeImage(String code, boolean drawline){
        //创建验证码图片
        BufferedImage rotateVcodeImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = rotateVcodeImage.createGraphics();
        //填充背景色
        g2d.setColor(COLOR);
        g2d.fillRect(0, 0, WIDTH, HEIGHT);
        if(drawline){
            drawDisturbLine(g2d);
        }
        //在图片上画验证码
        for(int i = 0; i < code.length(); i++){
            BufferedImage rotateImage = getRotateImage(code.charAt(i));
            g2d.drawImage(rotateImage, null, (int) (HEIGHT * 0.7) * i, 0);
        }
        g2d.dispose();
        return rotateVcodeImage;
    }

    /**
     * 获取一张旋转的图片
     * @param c 要画的字符
     * @return  图片
     */
    private static BufferedImage getRotateImage(char c){
        BufferedImage rotateImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = rotateImage.createGraphics();
        //设置透明度为0
        g2d.setColor(new Color(255, 255, 255, 0));
        g2d.fillRect(0, 0, WIDTH, HEIGHT);
        Random ran = new Random();
        g2d.setFont(new Font(FONT_NAMES[ran.nextInt(FONT_NAMES.length)], FONT_STYLES[ran.nextInt(FONT_STYLES.length)], FONT_SIZE));
        g2d.setColor(getRandomColor());
        double theta = getTheta();
        //旋转图片
        g2d.rotate(theta, WIDTH/2, HEIGHT/2);
        g2d.drawString(Character.toString(c), (HEIGHT-FONT_SIZE)/2, FONT_SIZE+5);
        g2d.dispose();

        return rotateImage;
    }

    /**
     * 画干扰线.
     * @param g 图像
     */
    private static void drawDisturbLine(Graphics g){
        Random ran = new Random();
        for(int i = 0;i < DISTURB_LINE_NUM; i++) {
            int x1 = ran.nextInt(WIDTH);
            int y1 = ran.nextInt(HEIGHT);
            int x2 = ran.nextInt(WIDTH);
            int y2 = ran.nextInt(HEIGHT);
            g.setColor(getRandomColor());
            g.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * @return 返回一个随机颜色
     */
    private static Color getRandomColor(){
        Random ran = new Random();
        return new Color(ran.nextInt(220), ran.nextInt(220), ran.nextInt(220));
    }

    /**
     * @return 角度
     */
    private static double getTheta(){
        return ((int) (Math.random()*1000) % 2 == 0 ? -1 : 1)*Math.random();
    }
}
